<?php

namespace App\Console\Commands\Invoice;

use App\Library\FeiShu\SendFs;
use App\Models\Invoice\InvoiceHistoryBusinessRelModel;
use App\Models\Invoice\InvoiceHistoryModel;
use App\Remote\MoreDo\InvoiceRemote;
use App\Service\Invoice\InvoiceService;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Kuafu\Snow\KfSnow;

class PollingHandleInvoiceBusinessCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'invoice:pollingHandleInvoiceBusiness';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '轮询处理开票业务';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     * @throws Exception|GuzzleException
     */
    public function handle(): int
    {
        logInfo("开票轮询开始执行", collect([]), true, false);

        $redisClient = getRedisClient();
        if ($redisClient->setnx($this->signature, true)) {
            try {
                //批量获取待开票与红冲中的数据
                InvoiceHistoryModel::query()
                                   ->whereIn("status", [
                                       InvoiceHistoryModel::STATUS_1,
                                       InvoiceHistoryModel::STATUS_4
                                   ])
                                   ->chunkById(
                                       20,
                                       function (Collection $collection) {
                                           $collection->each(function (InvoiceHistoryModel $invoiceHistoryModel) {
                                               try {

                                                   $collectLog = collect([
                                                       "orderId" => $invoiceHistoryModel->orderId,
                                                       "id"      => $invoiceHistoryModel->id
                                                   ]);

                                                   logInfo(
                                                       "开票处理",
                                                       collect([
                                                           "orderId" => $invoiceHistoryModel->orderId,
                                                           "id"      => $invoiceHistoryModel->id
                                                       ]),
                                                       true, false
                                                   );

                                                   $this->output->info($collectLog->toArray());

                                                   switch ($invoiceHistoryModel->status) {
                                                       case InvoiceHistoryModel::STATUS_1://待开票处理
                                                           $this->handleStatus_1($invoiceHistoryModel);
                                                           break;
                                                       case InvoiceHistoryModel::STATUS_4://红冲中处理
                                                           $this->handleStatus_4($invoiceHistoryModel);
                                                           break;
                                                   }
                                               } catch (Exception $exception) {
                                                   $errorMsg = $exception->getMessage();
                                                   if ($this->needReLogin($errorMsg) || $this->tokenLoseEffectiveness($errorMsg)) {
                                                       //{"ret":1,"msg":"电局账号【18291491952】登录失效，请稍后再试","data":null}
                                                       //登陆失效 ｜ token失效 此时直接抛出异常
                                                       throw $exception;
                                                   } else {
                                                       //基础业务异常 记录异常信息  开启下行记录处理
                                                       logException(
                                                           "开票异常 {$invoiceHistoryModel->id} - {$invoiceHistoryModel->status}",
                                                           $exception,
                                                           $invoiceHistoryModel->toArray(),
                                                           true, true
                                                       );
                                                   }
                                               }
                                           });
                                       });
            } catch (Exception $exception) {
                logException("{$this->description} 处理异常", $exception, null, true, false);
            }
            $redisClient->del($this->signature);
        } else {
            logBusinessError("{$this->description} ", "上次脚本暂未执行完毕", SendFs::ROBOT_BUSINESS_INVOICE_ERROR);
        }

        // qa主动拉取
        if (isTest()) {
            sleep(2);
            Artisan::call('invoice:initInvoiceStatus');
        }

        return 0;
    }

    /**
     * 待开票业务处理
     *
     * @param InvoiceHistoryModel $invoiceHistoryModel
     * @throws GuzzleException
     * @throws ValidationException
     */
    public function handleStatus_1(InvoiceHistoryModel $invoiceHistoryModel)
    {
        if ($invoiceHistoryModel->blueSerialNumber) {
            //todo 蓝票流水号不为空 代表已申请开票 等待盟度回调 此次不做处理
            return;
        }

        //todo 开票&重试
        $invoiceResponse = $this->retryApplyInvoice($invoiceHistoryModel);

        //todo 开票状态变更为开票中
        InvoiceHistoryModel::query()->where("id", $invoiceHistoryModel->id)->update([
            "blueSerialNumber"  => $invoiceResponse["code"],
            "blueInvoiceNumber" => $invoiceResponse["fphm"],
            "blueInvoiceTime"   => strtotime($invoiceResponse["kprq"]),
            "blueTaxValueTotal" => $invoiceResponse["jshj"],
            "blueEwm"           => $invoiceResponse["ewm"],
            "updatedAt"         => time()
        ]);
    }

    /**
     * 蓝票开具 & token失效重试 & 开具失败异常处理
     *
     * @param InvoiceHistoryModel $invoiceHistoryModel
     * @param int $retryTimes
     * @return mixed
     * @throws GuzzleException
     * @throws ValidationException
     */
    public function retryApplyInvoice(InvoiceHistoryModel $invoiceHistoryModel, int $retryTimes = 0)
    {
        try {
            //todo 发起开票
            return InvoiceService::invoice($invoiceHistoryModel->id);
        } catch (Exception $exception) {
            $errorMsg = $exception->getMessage();
            if ($this->tokenLoseEffectiveness($errorMsg) && $retryTimes <= 3) {
                //todo 增加token失效重试  最多三次
                $retryTimes++;
                return $this->retryApplyInvoice($invoiceHistoryModel, $retryTimes);
            } else {
                if ($this->needReLogin($errorMsg)) {
                    //todo 业务人员登陆校验失败
                    $this->logAccountError("开票业务流程阻断", $errorMsg);
                } elseif ($this->tokenLoseEffectiveness($errorMsg)) {
                    logBusinessError("登陆token失效！等待下次重试！", $errorMsg, SendFs::ROBOT_BUSINESS_INVOICE_ERROR);
                } else {
                    if (Str::contains($errorMsg, ["申请异常，请检查参数后重试"]) && $invoiceHistoryModel->retryTimes < 3) {
                        //todo 开票异常 增加重试
                        InvoiceHistoryModel::query()->where("id", $invoiceHistoryModel->id)->update([
                            "errorMsg"   => "开票等待下次重试 : " . $errorMsg,
                            "retryTimes" => DB::raw('retryTimes + 1'),
                            "updatedAt"  => time()
                        ]);
                    } else {
                        //todo 更新开票状态 -》 开票失败
                        InvoiceHistoryModel::query()->where("id", $invoiceHistoryModel->id)->update([
                            "errorMsg"  => "开票失败 : " . $errorMsg,
                            "status"    => InvoiceHistoryModel::STATUS_2,
                            "updatedAt" => time()
                        ]);

                        //todo 同步orm开票状态 - 开票中 -》 待开票
                        InvoiceService::syncOrmOrderInvoiceStatus($invoiceHistoryModel->id, 0);

                        logBusinessError("开票失败", vsprintf("开票主表id %s 重试次数 %s 错误信息 %s", [
                            $invoiceHistoryModel->id,
                            $invoiceHistoryModel->retryTimes,
                            $errorMsg
                        ]), SendFs::ROBOT_BUSINESS_INVOICE_ERROR);
                    }
                }
                throw  $exception;
            }
        }
    }

    /**
     * 红冲业务处理
     *
     * @param InvoiceHistoryModel $invoiceHistoryModel
     * @throws GuzzleException
     * @throws ValidationException
     */
    public function handleStatus_4(InvoiceHistoryModel $invoiceHistoryModel)
    {
        if ($invoiceHistoryModel->redConfirmSerialNo) {
            //todo 红字确认单流水号不为空 表示已经申请红冲等待回调 此次不做重复处理
            return;
        }

        //todo 开具红票
        $redInvoiceResponse = $this->retryApplyRedInvoice($invoiceHistoryModel);

        //todo 原蓝票发起红冲数据回写
        InvoiceHistoryModel::query()->where("id", $invoiceHistoryModel->id)->update([
            "redConfirmSerialNo" => $redInvoiceResponse[0]["redConfirmSerialNo"],
            "redConfirmNo"       => $redInvoiceResponse[0]["redConfirmNo"],
            "redConfirmUuid"     => $redInvoiceResponse[0]["redConfirmUuid"],
            "redConfirmState"    => $redInvoiceResponse[0]["confirmState"],
            "updatedAt"          => time()
        ]);
    }

    /**
     * 红票开具 & 重试 & 开具异常处理
     *
     * @param InvoiceHistoryModel $invoiceHistoryModel
     * @param int $retryTimes
     * @return mixed
     * @throws GuzzleException
     * @throws ValidationException
     */
    public function retryApplyRedInvoice(InvoiceHistoryModel $invoiceHistoryModel, int $retryTimes = 0)
    {
        try {
            //todo 发起红冲
            return InvoiceRemote::applyRedInvoice(
                $invoiceHistoryModel->orderId,
                $invoiceHistoryModel->blueInvoiceNumber,
                $invoiceHistoryModel->getInvoiceTypeTemplateText()
            );
        } catch (Exception $exception) {
            $errorMsg = $exception->getMessage();
            if ($this->tokenLoseEffectiveness($errorMsg) && $retryTimes <= 3) {
                //todo token失效发起重试 最多三次
                $retryTimes++;
                return $this->retryApplyRedInvoice($invoiceHistoryModel, $retryTimes);
            } else {
                if ($this->needReLogin($errorMsg)) {
                    //todo 电局账号失效 异常记录 脚本终止
                    $this->logAccountError("红冲业务流程阻断", $errorMsg);
                } elseif ($this->tokenLoseEffectiveness($errorMsg)) {
                    //todo token失效 终止脚本
                    logBusinessError("登陆token失效！等待下次重试！", $errorMsg, SendFs::ROBOT_BUSINESS_INVOICE_ERROR);
                } else {
                    //todo 业务提交异常 错误信息写入 & 红冲中 -》 已开票
                    InvoiceHistoryModel::query()->where("id", $invoiceHistoryModel->id)->update([
                        "errorMsg"     => "红冲失败:" . $errorMsg,
                        "status"       => InvoiceHistoryModel::STATUS_3,
                        "newInvoiceId" => 0,
                        "updatedAt"    => time()
                    ]);

                    //todo 换开基础数据变更为已删除
                    InvoiceHistoryModel::query()
                                       ->where("id", $invoiceHistoryModel->newInvoiceId)
                                       ->update(["isDeleted" => 1]);


                    //todo 同步orm开票状态 红冲中 -》 已开票
                    InvoiceService::syncOrmOrderInvoiceStatus($invoiceHistoryModel->id, InvoiceHistoryModel::STATUS_3);
                }

                //todo 异常抛出
                throw $exception;
            }
        }
    }

    /**
     * 登录税局APP扫脸认证
     *
     * @param string $errorMsg
     * @return bool
     */
    public function needReLogin(string $errorMsg): bool
    {
        if (Str::contains($errorMsg, ["70045", "电局账号", "18291491952"])) {
            return true;
        }
        return false;
    }

    /**
     * token失效
     *
     * @param string $errorMsg
     * @return bool
     */
    public function tokenLoseEffectiveness(string $errorMsg): bool
    {
        if (Str::contains($errorMsg, ["token"])) {
            return true;
        }
        return false;
    }

    /**
     * 获取飞书通知用户
     */
    public function getFsUserIds(): array
    {
        if (checkEnv('prod')) {
            return [
                '2c54915f' // 胡迪
            ];
        } else {
            return [
                'c32e1b91', // qy
                '9acce1b7', // 风雪
                '2c54915f', // 胡迪
            ];
        }
    }

    /**
     * 记录账号错误
     *
     * @param string $logTitle
     * @param string $errorMsg
     */
    public function logAccountError(string $logTitle, string $errorMsg)
    {
        $hi = (int)Carbon::now()->format("Hi");
        if ($hi >= 1720 && $hi <= 1820) {
            logBusinessError(
                $logTitle,
                $errorMsg,
                SendFs::ROBOT_BUSINESS_INVOICE_ERROR,
                $this->getFsUserIds()
            );
        }
    }
}
